package qf.index;

import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AdSearch {

    /**
     * WOS的作者地址字段AD的检索，主方法：doCheck(String c1, String kw)，c1即为c1的文本内容，kw为检索策略，必须为AD=的方式，程序有中判断是否为非法检索词（方法：checkGrammar(String kw)），因此，实际的调用的方法为：doCheck(c1, checkGrammar(kw))
     * 贾新志 2025.03.02
     * 本作者发文地址信息检索，支持逻辑关系符：SAME、AND、NOT、OR，以及截词符检索，比如：
     * AD=((Shan?dong First Medical University OR Shan?dong First Med Univ) SAME (Qianfoshan Hosp* OR Qian Foshan Hosp* OR Affiliated Hosp* 1, OR Hosp* 1,) SAME (jinan OR ji'nan OR 16766 Jingshi Rd))
     * 检索式中，截词符"?"表示0到1个字符，"*"表示0到N个字符，同一个检索条件中，截词符只能使用一种，不可混用。"SAME"表示限定在同一条作者发文地址信息中，而不是同一篇作者发文地址信息中。
     * 检索词"Hosp* 1,"和"Hosp* 1"，虽然只差了一个逗号，但意义是不一样的，不加逗号的有可能检索到"Hosp* 10"。
     * 各个逻辑关系符必须大写，且前后必须加上空格，后面的检索词，应加上英文的括号或者英文的引号，比如上面的检索策略也可以写成：
     * AD=""Shan?dong First Medical University OR Shan?dong First Med Univ" SAME "Qianfoshan Hosp* OR Qian Foshan Hosp* OR Affiliated Hosp* 1, OR Hosp* 1," SAME "jinan OR ji'nan OR 16766 Jingshi Rd""
     * 但所有的括号或引号，必须成对出现，且要么使用括号，要么使用引号，不可混合使用，否则可能出现意想不到的检索结果，本系统推荐使用可以区分左右的括号来构建检索策略，另外，本系统也支持检索词在前限定符在后的标准的PubMed的检索语法，比如：
     * (((Shan?dong First Medical University[AD] OR Shan?dong First Med Univ[AD])) AND ((Qianfoshan Hosp*[AD] OR Qian Foshan Hosp*[AD] OR Affiliated Hosp* 1,[AD] OR Hosp* 1,[AD])) AND ((jinan[AD] OR ji'nan[AD] OR 16766 Jingshi Rd[AD])))
     * 因PubMed官网并不支持SAME检索，本系统默认在本系统中执行此类检索策略时，均在同一条作者发文地址信息中进行检索，与使用SAME逻辑关系符效果一样。更多的支持的检索策略可以参考：
     * AD=((Fujian Med Univ OR Fu Jian Med Univ) SAME (Union Hosp OR XieHe Hosp OR Xie He Hosp))
     * Fujian Medical University Union Hospital[AD] OR Union Hospital, Fujian Medical University[AD]
     * 本作者发文地址信息检索，只支持检索地址字段AD，不支持作者或关键词等其它的字段检索。
     */


    public static void main(String[] args) {

//        String c1 = "Peking Tsinghua Ctr Life Sci, Beijing HospIJ 1000 Natl Clin Res Ctr Mental Disorders Beijing 100191 Peoples R China";
//        List<String> list = FileUtil.readLines("C:\\临时文件\\2025_02\\temp.txt", "utf-8");
//        for (String kw : list) {
//            String s = kw.contains("(") && kw.contains(")") ? kw.replace("(", "\"").replace(")", "\"") : kw;
//            String t = yinHaoToKuoHao(s);
//            System.out.println(kw.equals(t) + "| s:" + s + "| t:" + t + "|" + doCheck(c1, checkGrammar(kw)));
//        }

        String c1 = "anhui chest hosp, hefei, anhui, peoples r china";
        String kw = "AD=(Anhui Chest Hosp) OR AD=(Anhui Prov Chest Hosp) OR AD=(Chest Hosp Anhui)";
        boolean b = doCheck(c1, checkGrammar(kw));
        System.out.println(b);

    }

    public static String checkGrammar(String kw) {
        // 检查检索语法，如果语法错误，返回错误信息，如果语法正确，返回原来的检索策略，如果原来的检索策略是引号的先给转成了括号。2025.03.03
        kw = yinHaoToKuoHao(kw);
        String result = kw;
        if (kw.contains("AD=(")) {
            kw = kw.replaceAll(" SAME \\(([^)]+)\\)", " SAME [$1]");
            while (true) {
                kw = kw.replaceAll("AD=\\(\\(([^)]+)\\)([^)]+)\\)", "AD=[[$1]$2]");
                Matcher matcher = Pattern.compile("\\(([^)]+)\\)").matcher(kw);
                if (matcher.find()) {
                    String group = matcher.group(1);
                    kw = kw.replace("(" + group + ")", "[" + group + "]");
                } else {
                    break;
                }
            }
        }
        if (kw.contains("(") || kw.contains(")") || kw.contains("\"") || !kw.contains("AD=[") || kw.contains(" SAME ]") || kw.contains(" AND ]") || kw.contains(" NOT ]") || kw.contains(" OR ]")
                || kw.contains(" SAME]") || kw.contains(" AND]") || kw.contains(" NOT]") || kw.contains(" OR]")) {
            return "检索语法错误！出现不成对的括号或引号或其它语法错误。";
        }
        return result;
    }

    public static String yinHaoToKuoHao(String kw) {
        // 将AD检索中的英文引号，转换成英文括号。AD="Beijing Hosp," -> AD=(Beijing Hosp,)  2025.03.03
        kw = kw.replace("“", "\"").replace("”", "\"").replace("（", "(").replace("）", ")").replaceAll(" +", " ")
                .replaceAll("(?i)AD=\"", "AD=\"").replaceAll("(?i)AD=\\(", "AD=(").replaceAll("(?i) SAME \"", " SAME \"").replaceAll("(?i) SAME \\(", " SAME (").trim();
        if (kw.contains("AD=\"")) {
            kw = kw.replaceAll(" SAME \"([^\"]+)\"", " SAME ($1)");
            while (true) {
                kw = kw.replaceAll("AD=\"\"([^\"]+)\"([^\"]+)\"", "AD=(($1)$2)");
                Matcher matcher = Pattern.compile("\"([^\"]+)\"").matcher(kw);
                if (matcher.find()) {
                    String group = matcher.group(1);
                    kw = kw.replace("\"" + group + "\"", "(" + group + ")");
                } else {
                    break;
                }
            }
        }
        return kw;
    }

    // 检查逻辑检索式，是否符合C1，即是否是第一或通讯作者，如果是返回true，如果不是返回false。不支持引号检索，比如：AD="Beijing Hosp,"
    public static boolean doCheck(String c1, String kw) {
        if (null == kw || kw.length() < 5 || null == c1 || c1.length() < 5 || kw.contains("检索语法错误！出现不成对的括号或引号或其它语法错误。")) {
            return false;
        }
        kw = sameChange(kw);
        c1 = c1.toUpperCase();
        kw = reStr(kw);
        System.out.println("kw0: " + kw);
        if (!kw.contains(" NOT ") && !kw.contains(" AND ") && !kw.contains(" OR ")) {
            String result = (kw.startsWith("AD=À") && kw.endsWith("Á")) ? kw.substring("AD=À".length(), kw.length() - "Á".length()) : kw;
            if (c1.contains(result) || wildcardsFalse(c1, result)) {
                return true;
            }
        }

        for (int i = 0; i < kw.length(); i++) {
            if (kw.contains("AD=À") && kw.contains("Á")) {
                Matcher matcher = Pattern.compile("AD=À([^ÀÁ]+)Á").matcher(kw);
                while (matcher.find()) {
                    String search = matcher.group(1);
                    String logic = (c1.contains(search) || wildcardsFalse(c1, search)) ? "⬇TRUE" : "⬇FALSE";
                    kw = kw.replace("AD=À" + search + "Á", logic).replaceAll(" +", " ");
                }
            } else {
                break;
            }
        }
        kw = kw.replace("À", "(").replace("Á", ")").replace("⬇FALSE", "false").replace("⬇TRUE", "true").replace(" NOT ", " AND !");
        System.out.println("kw1: " + kw);
        return evaluateExpression(kw);
    }

    public static boolean evaluateExpression(String expr) {
        // 将字符串格式的逻辑运算式，转换为运算后的布尔值，比如字符串：true OR false AND false AND !false。此方法需要加依赖：<groupId>org.springframework</groupId>
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(expr);
        try {
            return Boolean.TRUE.equals(expression.getValue(Boolean.class));
        } catch (EvaluationException e) {
//            throw new RuntimeException(e);
        }
        return false;
    }

    // 增加对相邻检索SAME的支持
    public static String sameChange(String str) {
        String t = str.replaceAll(" +", " ").replace("（", "À").replace("）", "Á").replace(" =", "=").replace("= ", "=")
                .replace("(", "À").replace(")", "Á").replace("Á Á", "ÁÁ").replace("À À", "ÀÀ");
        String[] split = t.toUpperCase().replace("\"", "").trim().split("");
        int size = split.length;
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < size; i++) {
            if ("A".equals(split[i]) && size > (i + 3)) {
                if ("AD=À".equals(split[i] + split[i + 1] + split[i + 2] + split[i + 3])) {
                    StringBuilder ad = new StringBuilder();
                    int sum = 0;
                    for (int j = i; j < size; j++) {
                        ad.append(split[j]);
                        if ("À".equals(split[j])) {
                            sum++;
                        }
                        if ("Á".equals(split[j])) {
                            sum--;
                            if (sum == 0) {
                                break;
                            }
                        }
                    }
                    String replace = "";
                    if (ad.toString().contains(" SAME ")) { // AD=ÀQILU HOSP* SAME AD=ÀJINANÁ OR AD=ÀQINGDAOÁÁ    AD=ÀQI LU HOSP* SAME AD=ÀJINANÁ OR AD=ÀQINGDAOÁ SAME ABCÁ   // AD=ÀÀSHANGHAIÁ SAME ÀJIAO TONG UNIVÁ SAME ÀSCH MEDÁÁ
                        String[] list = ad.toString().replaceAll("À+", "À").replaceAll("Á+", "Á").split(" SAME ");
                        StringBuilder r = new StringBuilder();
                        for (String s : list) {
                            if (s.trim().length() > 1) {
                                if (s.startsWith("AD=À")) {
                                    if (s.endsWith("Á")) {
                                        r.append("À").append(Or(s.substring("AD=À".length(), s.length() - "Á".length()))).append("Á AND ");
                                    } else {
                                        r.append("À").append(Or(s.substring("AD=À".length()))).append("Á AND ");
                                    }
                                } else if (s.startsWith("À")) {
                                    if (s.endsWith("Á")) {
                                        r.append("À").append(Or(s.substring("À".length(), s.length() - "Á".length()))).append("Á AND ");
                                    } else {
                                        r.append("À").append(Or(s.substring("À".length()))).append("Á AND ");
                                    }
                                } else {
                                    if (s.endsWith("Á")) {
                                        r.append("À").append(Or(s.substring(0, s.length() - "Á".length()))).append("Á AND ");
                                    } else {
                                        r.append("À").append(Or(s)).append("Á AND ");
                                    }
                                }
                            }
                        }
                        if (r.length() > 5 && r.toString().endsWith(" AND ")) {
                            r = new StringBuilder(r.substring(0, r.length() - " AND ".length()));
                        }
                        replace = "À" + r + "Á";
                    } else {
                        replace = ad.toString();
                    }
                    result.append(replace);
                    i += ad.length() - 1;
                } else {
                    result.append(split[i]);
                }
            } else {
                result.append(split[i]);
            }
        }
        return addBracket(result.toString());
    }

    // 括号内的OR运算
    public static String Or(String str) {
        String[] split = str.replace("À", "").replace("Á", "").split(" OR ");
        StringBuilder r = new StringBuilder();
        for (String s : split) {
            r.append(" OR AD=À").append(s).append("Á");
        }
        if (r.length() > 4) {
            r = new StringBuilder(r.substring(4));
        }
        return r.toString();
    }

    // 统一规范检索式，要求所有的检索词均被括号括起来，如果没有括号的，给加上。加上后，再把检索式的括号给改成引号引起来，这里的引号使用特殊的字符串：À->左引号或左括号  Á->右引号或右括号
    public static String addBracket(String str) {
        str = str.toUpperCase().replaceAll(" +", " ").replace(" )", ")").replace("( ", "(").replace(" =", "=").replace("= ", "=").replace(")", "Á").replace("(", "À").trim();
        String[] split = str.split("");
        StringBuilder result = new StringBuilder();
        boolean bool = false;
        for (int i = 0; i < split.length; i++) {
            String t = split[i];
            if ("=".equals(t) && split.length > i + 1 && i > 1 && !"À".equals(split[i + 1]) && "D".equals(split[i - 1]) && "A".equals(split[i - 2])) {
                result.append(t);
                result.append("À");
                bool = true;
                continue;
            }
            if (bool) {
                if (" ".equals(t) || "Á".equals(t)) {
                    result.append("Á");
                    bool = false;
                }
            }
            result.append(t);
        }
        if (bool) {
            result.append("Á");
        }
        return result.toString();
    }

    // .*?表示：0或N个  .+?表示：1或N个，这里采用0或N个  例：String c1 = "Liao cheng Peoples Hosp, Liaocheng 252002, Shandong, Peoples R China.";   String str = "AD=(Liao*Cheng People* Hosp)";
    public static boolean wildcardsFalse(String c1, String result) {
        if (result.contains("*")) {
            String w = result.replace("*", ".*?"); // Liao*Cheng People* Hosp -> LIAO.*?CHENG PEOPLE.*? HOSP
            Matcher matcher = Pattern.compile(w).matcher(c1);  // c1:LIAO CHENG PEOPLES HOSP, LIAOCHENG 252002, SHANDONG, PEOPLES R CHINA.
            return matcher.find();
        } else if (result.contains("?")) {
            String w = result.replace("?", ".?"); // 0到1个
            Matcher matcher = Pattern.compile(w).matcher(c1);  //
            return matcher.find();
        }
        return false;
    }

    public static String reStr(String str) {
        return str.replaceAll(" +", " ").replace("À ", "À").replace(" Á", "Á").replace(" )", ")").replace("( ", "(").replace(" =", "=")
                .replace("= ", "=").replace("AD=⬇FALSE", "⬇FALSE").replace("AD=⬇TRUE", "⬇TRUE").trim();
    }


}
